Android实现倒计时的几种方案汇总

您所在的位置:网站首页 倒计时 动态 Android实现倒计时的几种方案汇总

Android实现倒计时的几种方案汇总

2023-04-14 07:49| 来源: 网络整理| 查看: 265

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

前言

关于倒计时可以说我们App开发中常见的一种场景了,比如Splash倒计时跳转首页,比如发送短信之后倒计时60秒显示等等。

关于倒计时的实现方式,大家可能有不同的做法,这里做一下总结看看你使用的是哪一种呢?

一、CountDownTimer的实现

直接上代码

//倒计时的方式一 fun countDownTimer() { var num = 60 timer = object : CountDownTimer((num + 1) * 1000L, 1000L) { override fun onTick(millisUntilFinished: Long) { YYLogUtils.w("当时计数:" + num) if (num == 0) { YYLogUtils.w("重新开始") num = 60 } else { num-- } } override fun onFinish() { YYLogUtils.w("倒计时结束了..." + num) } } timer?.start() } private var timer: CountDownTimer? = null override fun onDestroy() { super.onDestroy() timer?.cancel() } 复制代码

没什么花活,就是android.os包下面的 CountDownTimer 类的使用。内部实现使用了 Handler 进行封装。

二、直接用Handler的实现 private var handlerNum = 60 private val mHandler = object : Handler(Looper.getMainLooper()) { override fun handleMessage(msg: Message) { when (msg.what) { 1 -> { if (handlerNum > 0) { handlerNum-- YYLogUtils.w("当时计数:" + handlerNum) countDownHander() } else { stopCountDownHander() } } } } } override fun onDestroy() { super.onDestroy() stopCountDownHander() } fun countDownHander() { mHandler.sendEmptyMessageDelayed(1, 1000) } fun stopCountDownHander() { mHandler.removeCallbacksAndMessages(null) } 复制代码

我们可以直接使用Handler的延时发送消息实现倒计时。

当然另一种做法是使用 Runnable 来实现。

Handler handler = new Handler(); Runnable runnable = new Runnable() { @Override public void run() { recLen++; txtView.setText("" + recLen); handler.postDelayed(this, 1000); } public void test(){ handler.postDelayed(runnable, 1000); } 复制代码 三、直接用Time、TimeTask的实现

以上是Android的倒计时方案,其实Java的Api也是支持倒计时实现的,比如 Timer 配合 TimerTask 就可以实现简单的倒计时。

fun countDownTimer2() { var num = 60 val timer = Timer() val timeTask = object : TimerTask() { override fun run() { num-- YYLogUtils.w("当时计数:" + num) if (num < 0) { timer.cancel() } } } timer.schedule(timeTask, 1000, 1000) } 复制代码 四、使用Theard倒计时

我们可以通过Thread的sleep方法来实现倒计时,不过由于是子线程我们不能更新UI,所以还是需要配合Handler实现。

private var mThread: Thread = Thread(this) private var mflag = false private var mThreadNum = 60 override fun run() { while (mflag && mThreadNum >= 0) { try { Thread.sleep(1000) } catch (e: InterruptedException) { e.printStackTrace() } val message = Message.obtain() message.what = 1 message.arg1 = mThreadNum handler.sendMessage(message) mThreadNum-- } } private val handler = Handler(Looper.getMainLooper()) { msg -> if (msg.what == 1) { val num = msg.arg1 //由于需要主线程显示UI,这里使用Handler通信 YYLogUtils.w("当时计数:" + num) } true } //开启倒计时 fun countDownThread() { if (!mThread.isAlive) { mflag = true if (mThread.state == Thread.State.TERMINATED) { mThread = Thread(this@DemoCountDwonActivity) if (mThreadNum == -1) mThreadNum = 60 mThread.start() } else { mThread.start() } } else { mflag = false } } override fun onDestroy() { super.onDestroy() mflag = false } 复制代码

这里的销毁线程我没有使用stop方法,已经不推荐我们使用,我们使用flag来判断即可。

五、使用框架RxJava

这样的线程并不是我们想要的,我们通常并不会直接new Thread 来进行一些逻辑操作,比如我们可能使用RxJava框架,通过操作符的方式来进行倒计时。

比我们倒计时4秒之后跳转页面的实现:

val SHOTDOWN_TIME = 4 val mDisposables : Disposable? = null Observable.interval(0, 1, TimeUnit.SECONDS) .take(SHOTDOWN_TIME.toLong()) .map { return@map SHOTDOWN_TIME - it } .observeOn(AndroidSchedulers.mainThread()) .subscribe({ LogUtil.e(it.toString()) }, { it.printStackTrace() }, { checkJump() }, { mDisposable = it }) override fun onDestroy() { super.onDestroy() mDisposable?.dispose() } 复制代码

注意我们还是需要通过mDisposable对象在页面销毁的时候释放,以免内存泄露,有没有简单一点方式?

六、Kotlin Flow 的实现

上面的方法都需要销毁资源,好麻烦,能不能自动取消?协程不就行了。

是的 lifecycleScope 根据生命周期自动取消的协程作用域,配合Flow的操作符完成倒计时岂不是完美。

好吧,你是自动倒计时了。结束之后取消协程,销毁也能取消协程,那如果我想手动的取消倒计时怎么办?比如倒计时60秒我就要在第50秒的时候强制取消协程怎么办?

launch方法返回的不就是Job 对象吗?根据此上下文对象不就可以取消协程了吗?

看看灵活的Flow倒计时如何实现。

定义一个扩展方法:

/** * 倒计时的实现 */ @ExperimentalCoroutinesApi fun FragmentActivity.countDown( time: Int = 5, start: (scop: CoroutineScope) -> Unit, end: () -> Unit, next: (time: Int) -> Unit ) { lifecycleScope.launch { // 在这个范围内启动的协程会在Lifecycle被销毁的时候自动取消 flow { (time downTo 0).forEach { delay(1000) emit(it) } }.onStart { // 倒计时开始 ,在这里可以让Button 禁止点击状态 start(this@launch) }.onCompletion { // 倒计时结束 ,在这里可以让Button 恢复点击状态 end() }.catch { //错误 YYLogUtils.e(issage ?: "Unkown Error") }.collect { // 在这里 更新值来显示到UI next(it) } } } 复制代码

使用:

fun startCountDown() { var timeDownScope: CoroutineScope? = null countDown( time = 60, start = { timeDownScope = it YYLogUtils.e("开始") }, end = { YYLogUtils.e("结速倒计时") toast("结速倒计时") }, next = { YYLogUtils.w("当时计数:" + it) if (it == 50) { timeDownScope?.cancel() } }) } 复制代码

无需onDestory中销毁资源,如果想自由手动的控制倒计时,我们再start的高阶函数中接收父协程的上下文对象即可自动控制。使用起来也是超级简单。

总结

倒计时的实现是我们常用的功能,如果你的项目是Kotlin构建的,那么我建议使用Flow来实现这种功能,使用扩展函数进行封装,使用起来更加的简单。

如果你们项目是Java语言实现的,那么同样的可以选择一种方式进行一个工具类的封装,也能达到同样的效果,只是记得需要在onDestory中销毁资源哦。

如有错漏还望指出,如有更多或更好的方法,也可以在评论区交流讨论。

如果感觉本文对你有一点点点的启发,还望你能点赞支持一下,你的支持是我最大的动力。

Ok,这一期就此完结。



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3